package com.ly.mp.dal.comm.config;

import java.sql.SQLException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.shardingsphere.api.config.sharding.ShardingRuleConfiguration;
import org.apache.shardingsphere.api.config.sharding.TableRuleConfiguration;
import org.apache.shardingsphere.api.config.sharding.strategy.InlineShardingStrategyConfiguration;
import org.apache.shardingsphere.api.config.sharding.strategy.StandardShardingStrategyConfiguration;
import org.apache.shardingsphere.shardingjdbc.api.ShardingDataSourceFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import com.alibaba.druid.pool.DruidDataSource;
import com.ly.mp.dal.comm.config.condition.TransactionPolicyShard;
import com.ly.mp.dal.comm.jdbc.DynamicDataSource;

import io.seata.rm.datasource.DataSourceProxy;
import io.seata.spring.annotation.GlobalTransactionScanner;

@Configuration
@Conditional({TransactionPolicyShard.class})
@EnableTransactionManagement(proxyTargetClass = true)
public class MpDataBaseShardConfiguration {
    private static final Logger logger = LoggerFactory.getLogger(MpDataBaseShardConfiguration.class);
    @Value("${seata.txgroup:my_test_tx_group}")
    private String txGroup;
    @Value("${seata.apolloAppId:seata-server}")
    private String appId;

    @Bean(name = {"dataSource"})
    public DataSource dynamicDataSource(BaseDataSourceInfo baseInfo, DatabaseSet dbSet,
            ShardingRuleConfiguration shardingRuleConfig) {
        Map<Object, Object> targetDataSources = new HashMap();
        DynamicDataSource dataSource = new DynamicDataSource();

        try {
            Map<String, DataSource> shardDatasources = new LinkedHashMap();
            DataSource mpDatasource = this.createDataSourceMap(baseInfo, dbSet, shardDatasources);
            DataSource shardingDataSource = ShardingDataSourceFactory.createDataSource(shardDatasources,
                    shardingRuleConfig, new Properties());
            if (mpDatasource != null) {
                targetDataSources.put("default_writeDBKey", mpDatasource);
                targetDataSources.put("shard_writeDBKey", shardingDataSource);
            } else {
                targetDataSources.put("default_writeDBKey", shardingDataSource);
            }
        } catch (SQLException var9) {
            logger.error("读取数据源出错, ", var9);
        }

        dataSource.setDefaultTargetDataSource(targetDataSources.get(dbSet.getDefaultKey()));
        dataSource.setTargetDataSources(targetDataSources);
        return dataSource;
    }

    ShardingRuleConfiguration getShardingRuleConfiguration(DatabaseSet dbSet) {
        ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
        shardingRuleConfig.setDefaultDatabaseShardingStrategyConfig(
                new InlineShardingStrategyConfiguration("user_id", "ds${user_id % 2}"));
        TableRuleConfiguration orderTableRuleConfiguration = new TableRuleConfiguration("t_order",
                "ds${0..1}.t_order${0..1}");
        shardingRuleConfig.getTableRuleConfigs().add(orderTableRuleConfiguration);
        shardingRuleConfig.setDefaultTableShardingStrategyConfig(
                new StandardShardingStrategyConfiguration("order_id", new PreciseModuloShardingTableAlgorithm()));
        return shardingRuleConfig;
    }

    private DataSource createDataSourceMap(BaseDataSourceInfo baseInfo, DatabaseSet dbSet,
            Map<String, DataSource> shardDatasources) {
        DataSource mpDatasource = null;
        List<DataSourceInfo> infoList = dbSet.getDataSourceInfo();
        int i = 0;
        Iterator var7 = infoList.iterator();

        while (var7.hasNext()) {
            DataSourceInfo dbInfo = (DataSourceInfo) var7.next();
            DruidDataSource druidDataSource = new DruidDataSource();
            BeanUtils.copyProperties(baseInfo, druidDataSource);
            BeanUtils.copyProperties(dbInfo, druidDataSource);
            if (dbInfo.getUrl().contains("oracle")) {
                druidDataSource.setValidationQuery("select 1 from dual");
            } else {
                druidDataSource.setValidationQuery("select 1");
            }

            try {
                DataSourceProxy dsProxy = new DataSourceProxy(druidDataSource);
                druidDataSource.init();
                if (i == 0 && dbInfo.getOrigName().toLowerCase().startsWith("mp")) {
                    mpDatasource = dsProxy;
                } else {
                    shardDatasources.put(dbInfo.getOrigName(), dsProxy);
                }

                ++i;
            } catch (SQLException var11) {
                logger.error(var11.getMessage(), var11);
            }
        }

        return mpDatasource;
    }

    @Bean
    public GlobalTransactionScanner globalTransactionScanner(Environment environment) {
        String modelName = System.getProperty("service.name");
        String appName = Objects.isNull(modelName) ? "default" : modelName;
        String zkAddress = environment.getProperty("zookeeper.address");
        String sessionTime = environment.getProperty("zookeeper.session.timeout");
        String connectTime = environment.getProperty("zookeeper.connect.timeout");
        System.setProperty("zkAddress", zkAddress);
        System.setProperty("sessionTime", sessionTime);
        System.setProperty("connectTime", connectTime);
        GlobalTransactionScanner globalTransactionScanner = new GlobalTransactionScanner(appName, this.txGroup);
        return globalTransactionScanner;
    }

    @Bean
    public PlatformTransactionManager transactionManager(DynamicDataSource dataSource) {
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }
}